home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Utilities / Workspace / PackageInspector0.95 / Source / Subprocess.m < prev   
Text File  |  1995-07-28  |  8KB  |  344 lines

  1. /*
  2.     Subprocess.m    (v10)
  3.     by Charles L. Oei
  4.     pty support by Joe Freeman
  5.     Subprocess Example, Release 2.0
  6.     NeXT Computer, Inc. 
  7.  
  8.     You may freely copy, distribute and reuse the code in this example.
  9.     NeXT disclaims any warranty of any kind, expressed or implied, as to
  10.     its fitness for any particular use.
  11. */
  12.  
  13. #import "Subprocess.h"
  14. // #import <sgtty.h>    // needed to compile under Release 1.0
  15. #import <appkit/nextstd.h>
  16. #import <appkit/Application.h>
  17. #import <appkit/Panel.h>
  18. #import <sys/wait.h>
  19.  
  20. #define    PTY_TEMPLATE "/dev/pty??"
  21. #define    PTY_LENGTH 11
  22.  
  23. static void showError();
  24.  
  25.  
  26. /*==========================================================
  27.  *
  28.  * Private Instance Methods
  29.  *
  30.  *==========================================================*/
  31.  
  32. @interface Subprocess(Private)
  33. - childDidExit;
  34. - fdHandler:(int)theFd;
  35. @end
  36.  
  37. @implementation Subprocess(Private)
  38.  
  39. - childDidExit
  40.     // cleanup after a child process exits
  41. {
  42.     if (childPid)
  43.     {
  44.     union wait exitstatus;
  45.     int waitresult;
  46.  
  47.     DPSRemoveFD(fromChild);
  48.     close(fromChild);
  49.     fclose(fpToChild);
  50.     // Cleanup zombie processes. (blocking wait is too dangerous here...)
  51.     waitresult = wait4(childPid, &exitstatus, WNOHANG, NULL);
  52.     if (waitresult != childPid) {
  53.         /* XXX should handle this gracefully, e.g, timed entry. */
  54.     }
  55.     childPid=0;    // specify that child is dead
  56.     if (delegate)
  57.     {    
  58.         if ([delegate respondsTo:@selector(subprocessDone:)])
  59.         [delegate perform:@selector(subprocessDone:) with:self];
  60.         else if ([delegate respondsTo:@selector(subprocessDone)])
  61.         [delegate perform:@selector(subprocessDone)];
  62.     }
  63.     }
  64.     return self;
  65. }
  66.  
  67. - fdHandler:(int)theFd
  68.     // DPS handler for output from subprocess
  69. {
  70.     if ((bufferCount = read(theFd, outputBuffer, BUFFERSIZE-1)) <= 0)
  71.     {
  72.     [self childDidExit];
  73.     return self;
  74.     }
  75.     outputBuffer[bufferCount] = '\0';
  76.     if (delegate)
  77.     {
  78.         if ([delegate respondsTo:@selector(subprocess:output:)])
  79.         [delegate perform:@selector(subprocess:output:)
  80.           with:self with:(void *)&outputBuffer];
  81.         else if ([delegate respondsTo:@selector(subprocessOutput:)])
  82.         [delegate perform:@selector(subprocessOutput:)
  83.           with:(void *)&outputBuffer];
  84.     }
  85.     return self;
  86. }
  87.  
  88. @end
  89.  
  90.  
  91. /*==========================================================
  92.  *
  93.  * Private Utility Routines
  94.  *
  95.  *==========================================================*/
  96.  
  97. static void
  98. showError (const char *errorString, id theDelegate)
  99.     // ensure errors never get dropped on the floor
  100. {
  101.     if (theDelegate && [theDelegate respondsTo:@selector(subprocessError:)])
  102.     [theDelegate
  103.         perform:@selector(subprocessError:)
  104.         with:(void *)errorString];
  105.     else if (NXApp)    // no delegate, but we're running w/in an App
  106.     NXRunAlertPanel(0, errorString, 0, 0, 0);
  107.     else
  108.     perror(errorString);
  109. }
  110.  
  111. static void
  112. fdHandler (int theFd, id self)
  113.     // DPS handler for output from subprocess
  114. {
  115.     [self fdHandler:theFd];
  116. }
  117.  
  118. static void
  119. getptys (int *master, int *slave)
  120.     // attempt to setup the ptys
  121. {
  122.     char device[PTY_LENGTH];
  123.     char *block, *num;
  124.     char *blockLoc; // specifies the location of block for the device string
  125.     char *numLoc; // specifies the pty name with the digit ptyxD
  126.     char *msLoc; // specifies the master (ptyxx) or slave (ttyxx)
  127.     
  128.     struct sgttyb setp =
  129.     {B9600, B9600, (char)0x7f, (char)0x15, (CRMOD|ANYP)};
  130.     struct tchars setc =
  131.     {CINTR, CQUIT, CSTART, CSTOP, CEOF, CBRK};
  132.     struct ltchars sltc =
  133.     {CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT};
  134.     int    lset =
  135.     (LCRTBS|LCRTERA|LCRTKIL|LCTLECH|LPENDIN|LDECCTQ);
  136.     int    setd = NTTYDISC;
  137.     
  138.     strcpy(device, PTY_TEMPLATE); // string constants are not writable
  139.     blockLoc = &device[ strlen("/dev/pty") ];
  140.     numLoc = &device[ strlen("/dev/pty?") ];
  141.     msLoc = &device[ strlen("/dev/") ];
  142.     for (block = "pqrs"; *block; block++)
  143.     {
  144.     *blockLoc = *block;
  145.     for (num = "0123456789abcdef"; *num; num++)
  146.     {
  147.         *numLoc = *num;
  148.         *master = open(device, O_RDWR);
  149.         if (*master >= 0)
  150.         {
  151.         *msLoc = 't';
  152.         *slave = open(device, O_RDWR);
  153.         if (*slave >= 0)
  154.         {
  155.             (void) ioctl(*slave, TIOCSETP, (char *)&setp);
  156.             (void) ioctl(*slave, TIOCSETC, (char *)&setc);
  157.             (void) ioctl(*slave, TIOCSETD, (char *)&setd);
  158.             (void) ioctl(*slave, TIOCSLTC, (char *)&sltc);
  159.             (void) ioctl(*slave, TIOCLSET, (char *)&lset);
  160.             return;
  161.         } else {
  162.             // close the master and reset the device
  163.             // name so that the master opens it properly
  164.             *msLoc = 'p';
  165.             close(*master);
  166.         }
  167.         }
  168.     } /* hunting through a bank of ptys */
  169.     } /* hunting through blocks of ptys in all the right places */
  170.     *master = -1;
  171.     *slave = -1;
  172. }
  173.  
  174.  
  175. @implementation Subprocess
  176.  
  177. /*==========================================================
  178.  *
  179.  * Public Instance Methods
  180.  *
  181.  *==========================================================*/
  182.  
  183. - init:(const char *)subprocessString
  184.     // a cover for the below withDelegate:nil, andPtySupport:NO, andStdErr:YES
  185. {
  186.     return
  187.     [self
  188.         init:subprocessString
  189.         withDelegate:nil
  190.         andPtySupport:NO
  191.         andStdErr:YES];
  192. }
  193.  
  194. - init:(const char *)subprocessString
  195.     withDelegate:theDelegate
  196.     andPtySupport:(BOOL)wantsPty
  197.     andStdErr:(BOOL)wantsStdErr
  198.     // initializes an instance of Subprocess and corresponding UNIX process
  199. {
  200.     int pipeTo[2];        // for non-Pty support
  201.     int pipeFrom[2];
  202.     int    tty, numFds, fd;    // for temporary use
  203.     int processGroup;
  204.     int pidChild;        // needed because childPid does not exist
  205.                 // until Subprocess is instantiated
  206.  
  207.     if (wantsPty)
  208.     {
  209.         tty = open("/dev/tty", O_RDWR);
  210.     getptys(&masterPty,&slavePty);
  211.     if (masterPty <= 0 || slavePty <= 0)
  212.     {
  213.         showError("Error grabbing ptys for subprocess.", theDelegate);
  214.         return self;
  215.     }
  216.     // remove the controlling tty if launched from a shell,
  217.     // but not Workspace;
  218.     // so that we have job control over the parent application in shell
  219.     // and so that subprocesses can be restarted in Workspace
  220.     if  ((tty<0) && ((tty = open("/dev/tty", 2))>=0))
  221.     {
  222.         ioctl(tty, TIOCNOTTY, 0);
  223.         close(tty);
  224.     }
  225.     }
  226.     else
  227.     {
  228.     if (pipe(pipeTo) < 0 || pipe(pipeFrom) < 0)
  229.     {
  230.         showError("Error starting UNIX pipes to subprocess.", theDelegate);
  231.         return self;
  232.     }
  233.     }
  234.     
  235.     switch (pidChild = vfork())
  236.     {
  237.     case -1:    // error
  238.     showError("Error starting UNIX vfork of subprocess.", theDelegate);
  239.     return self;
  240.  
  241.     case 0:    // child
  242.     if (wantsPty)
  243.     {
  244.         dup2(slavePty, 0);
  245.         dup2(slavePty, 1);
  246.         if (wantsStdErr)
  247.         dup2(slavePty, 2);
  248.     }
  249.     else
  250.     {
  251.         dup2(pipeTo[0], 0);
  252.         dup2(pipeFrom[1], 1);
  253.         if (wantsStdErr)
  254.         dup2(pipeFrom[1], 2);
  255.     }
  256.     
  257.     numFds = getdtablesize();
  258.     for (fd=3; fd<numFds; fd++)
  259.         close(fd);
  260.  
  261.     processGroup = getpid();
  262.     ioctl(0, TIOCSPGRP, (char *)&processGroup);
  263.     setpgrp (0, processGroup);
  264.     
  265.     // we exec a /bin/sh so that cmds are easier to specify for the user
  266.     execl("/bin/sh", "sh", "-c", subprocessString, 0);
  267.     perror("vfork (child)"); // should never gets here tho
  268.     exit(1);
  269.  
  270.     default:    // parent
  271.     [self setDelegate:theDelegate];
  272.     childPid = pidChild;
  273.  
  274.     if (wantsPty)
  275.     {
  276.         close(slavePty);
  277.         
  278.         fpToChild = fdopen(masterPty, "w");
  279.         fromChild = masterPty;
  280.     }
  281.     else
  282.     {
  283.         close(pipeTo[0]);
  284.         close(pipeFrom[1]);
  285.     
  286.         fpToChild = fdopen(pipeTo[1], "w");
  287.         fromChild = pipeFrom[0];
  288.     }
  289.  
  290.     setbuf(fpToChild, NULL);
  291.     DPSAddFD(
  292.         fromChild,
  293.         (DPSFDProc)fdHandler,
  294.         (id)self,
  295.         NX_MODALRESPTHRESHOLD+1);
  296.     return self;
  297.     }
  298. }
  299.  
  300. - send:(const char *)string withNewline:(BOOL)wantNewline
  301. {
  302.     fputs(string, fpToChild);
  303.     if (wantNewline)
  304.         fputc('\n', fpToChild);
  305.     return self;
  306. }
  307.  
  308. - send:(const char *)string
  309. {
  310.     [self send:string withNewline:YES];
  311.     return self;
  312. }
  313.  
  314. - terminateInput
  315.     // effectively sends an EOF to the child process stdin
  316. {
  317.     fclose(fpToChild);
  318.     return self;
  319. }
  320.  
  321. - terminate:sender
  322. {
  323.     if (childPid)
  324.     {
  325.     //kill(childPid+1, SIGTERM);
  326.     killpg(childPid, SIGTERM);
  327.     [self childDidExit];
  328.     }
  329.     return self;
  330. }
  331.  
  332. - setDelegate:anObject
  333. {
  334.     delegate = anObject;
  335.     return self;
  336. }
  337.  
  338. - delegate
  339. {
  340.     return delegate;
  341. }
  342.  
  343. @end
  344.